<html>
<head>
<!--
 | jsMath-global.html
 |
 | Part of the jsMath package for mathematics on the web.
 | 
 | This file is used to store persistent data across multiple
 | documents, for example, the cookie data when files are loaded
 | locally, or cached typset math data.
 |
 | The file opens a frameset with one frame containing the
 | actual document to view, and stores the global data in the
 | outer document.  That way, when the frame changes documents
 | the global data is still available.
 | 
 | ---------------------------------------------------------------------
 | 
 | Copyright 2006 by Davide P. Cervone
 |
 | Licensed under the Apache License, Version 2.0 (the "License");
 | you may not use this file except in compliance with the License.
 | You may obtain a copy of the License at
 |
 |     http://www.apache.org/licenses/LICENSE-2.0
 |
 | Unless required by applicable law or agreed to in writing, software
 | distributed under the License is distributed on an "AS IS" BASIS,
 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | See the License for the specific language governing permissions and
 | limitations under the License.
-->
<title>jsMath Global Frame</title>
<style></style>

<script>
var jsMath = {
  isGlobal:  1,  // to pervent FRAME from inheriting this jsMath
  isOpera:   (window.opera != null && window.Packages != null),
  isSafari:  (window.clientInformation && document.implementation.createCSSStyleSheet != null),
  isMSIEmac: (screen.updateInterval != null && !window.SyntaxError && !window.print),
  msieDelay: 1000,  // MSIE/mac delay time for checking for offsite pages

  /***************************************************************/

  /*
   *  This will be shared with the jsMath in the frameset
   */
  Global: {
    isHidden: 0,
    cookie: '',
    cache: {T: {}, D: {}, R: {}, B: {}},
    ClearCache: function () {jsMath.Global.cache = {T: {}, D: {}, R: {}, B: {}}},
    Init: function () {}
  },

  /*
   *  Called by jsMath to attach to the Global data area.
   */
  Register: function (w) {
    w.jsMath.Global = jsMath.Global;
    w.jsMath.Controls.localSetCookie = jsMath.localSetCookie;
    w.jsMath.Controls.localGetCookie = jsMath.localGetCookie;
    if (window.location.protocol != 'mk:') {
      document.title = w.document.title;
      this.ShowURL(w.location);
      jsMath.SetUnload();
    }
  },

  /***************************************************************/

  /*
   *  Initialize the global data and load the contents of the frame
   */
  Init: function () {
    if (this.inited) return;
    this.controls = document.getElementById('jsMath_controls');
    this.frame    = document.getElementById('jsMath_frame');
    this.window   = this.FrameWindow();
    var URL;
    if (!this.isOpera) {try {URL = this.window.document.URL} catch (err) {}}
    if (!URL || URL == "about:blank" || URL.match(/jsMath-global.html$/))
      {this.frame.src = this.src}

    this.Global.frame = this.frame;
    this.Global.Show = this.Show;
    this.Global.GoLocal = this.GoLocal;
    if (this.hide) {this.Hide()}
    this.inited = 1;
  },

  GetURL: function () {
    var src = unescape(document.location.search.substr(1));
    if (src == '') {src = unescape(document.location.hash.substr(1))}
    src = src.replace(/\?(.*)/,'');
    if (RegExp.$1) {this.Global.cookie = unescape(RegExp.$1)}
    return src;
  },

  FrameWindow: function (force) {
    if (this.window && !force) {return this.window}
    if (this.frame.contentWindow) {return this.frame.contentWindow}
    if (document.frames && document.frames.jsMath_frame) 
      {return document.frames.jsMath_frame}
    return null;
  },

  /*
   *  Called whenever the FRAME loads a new file.
   *  (Update the title, etc.)
   */
  Loaded: function () {
    if (!jsMath.inited) {jsMath.Init()}
    if (!jsMath.window) {jsMath.window = jsMath.FrameWindow()}
    if (!jsMath.window || window.location.protocol == 'mk:') return;
    var URL; try {URL = jsMath.frame.src} catch (err) {}
    var title; try {title = jsMath.window.document.title} catch (err) {}
    if (URL != null && URL == jsMath.URL) return;
    if (title != null) {
      document.title = title;
      jsMath.ShowURL(jsMath.window.location);
      jsMath.SetUnload();
    } else {
      jsMath.Offsite();
    }
  },

  /*
   *  Mark the page as offsite and can't be read
   */
  Offsite: function () {
    document.title = "jsMath Global: Offsite document -- can't read title";
    jsMath.ShowURL("(Offsite document -- cant' read URL)");
    if (jsMath.print && !jsMath.Global.isHidden) {
      jsMath.print.disabled  = true;
      jsMath.reload.disabled = true;
    }
    jsMath.window = null;
  },

  //
  //  Safari doesn't fire onload for offsite URL's, so use in unload
  //  handler to look for these and mark them.
  //  MSIE/mac doesn't fire onload events at all, so use unload
  //  handler to simulate them.
  //
  SetUnload: function () {
    if (jsMath.isMSIEmac || jsMath.isSafari) {
      try {jsMath.oldUnload = jsMath.window.unload} catch (err) {}
      try {jsMath.window.onunload = jsMath.Unload} catch (err) {}
    }
  },
  Unload: function (event) {
    if (jsMath.oldUnload) {jsMath.oldUnload(event)}
    try {setTimeout('jsMath.StateChange(0)',1)} catch (err) {}
  },
  StateChange: function (i) {
    jsMath.ShowURL(jsMath.window.location);
    var state = 'unknown'; try {state = jsMath.window.document.readyState} catch (err) {};
    if (state == 'unknown' || i++ == 20) {jsMath.Offsite(); return}
    if (state != 'complete') {setTimeout('jsMath.StateChange('+i+')',50*i); return}
    document.title = jsMath.window.document.title;
    jsMath.ShowURL(jsMath.window.location);
    if (window.location.host == jsMath.window.location.host) 
      {jsMath.SetUnload(); return}
    if (jsMath.isMSIEmac) {
      jsMath.oldLocation = jsMath.window.location;
      setTimeout('jsMath.MSIEcheckDocument()',jsMath.msieDelay);
    }
  },
  MSIEcheckDocument: function () {
    if (window.location.host == jsMath.window.location.host) {
      jsMath.StateChange(0)
    } else {
      if (jsMath.oldLocation != jsMath.window.location) {
        jsMath.ShowURL(jsMath.window.location);
        document.title = jsMath.window.document.title;
      }
      setTimeout('jsMath.MSIEcheckDocument()',jsMath.msieDelay);
    }
  },

  /*
   *  Replacements for standard localSetCookie/localGetCookie that
   *  use the global cache for storing the cookie data rather than
   *  the document.location's search field.
   */
  localSetCookie: function (cookie,warn) {
    if (cookie != "") {cookie = 'jsMath='+cookie}
    if (cookie == jsMath.Global.cookie) return;
    jsMath.Global.cookie = cookie;
  },
  localGetCookie: function () {return (jsMath.Global.cookie || "")},

  /*
   *  Set the URL in the controls window
   */
  ShowURL: function (URL) {
    try {
      jsMath.URL = URL; if (jsMath.url) {jsMath.url.value = URL}
      if (jsMath.print && !jsMath.Global.isHidden) {
        jsMath.print.disabled  = !window.print;
        jsMath.reload.disabled = false;
      }
    } catch (err) {}
  },
  SetURL: function () {this.frame.src = jsMath.url.value},

  /*
   *  Handle actions for the document frame
   */
  Print: function () {if (this.window) {this.window.document.body.focus(); this.window.print()}},
  Reload: function () {if (this.window) {this.window.location.reload()}},
  GoLocal: function () {
    if (jsMath.window) {
      jsMath.UpdateCookie();
      if (jsMath.isMSIEmac) {
        alert("MSIE/Mac has a bug that causes it not to go local properly.  "
            + "After you press OK below, your browser will appear to hang.  "
            + "When this happens, press Command-. to cancel the action.  "
            + "The window should clear and the page location will appear "
            + "in the address area at the top of the screen.  Press the "
            + "REFRESH button to load the page correctly.");
      }
      jsMath.location = jsMath.window.location;
      if (jsMath.window.location.protocol == 'file:' && jsMath.Global.cookie)
        {jsMath.location += '?' + escape(jsMath.Global.cookie)}
      setTimeout('window.location = jsMath.location',1);
      return;
    }
    alert("You are viewing a web page from a site other than the "
        + "one where you loaded jsMath-Global, so jsMath can't read "
        + "its URL to load it locally.\n\n"
        + "Check to see if your browser has a contextual menu item to "
        + "open the active frame in a separate window.");
  },

  /*
   *  Read the cookie data, set the hiddenGlobal and global fields
   *  and save the cookie again.
   */
  UpdateCookie: function () {
    var cookie = []; var cookies = jsMath.window.document.cookie;
    if (window.location.protocol == 'file:') {cookies = jsMath.Global.cookie}
    if (cookies.match(/jsMath=([^;]+)/)) {
      var data = RegExp.$1.split(/,/);
      for (var i = 0; i < data.length; i++) {
        var x = data[i].match(/(.*):(.*)/);
        if (x[2].match(/^\d+$/)) {x[2] = 1*x[2]} // convert from string
        cookie[x[1]] = x[2];
      }
    }
    cookie.hiddenGlobal = jsMath.Global.isHidden;
    cookie.global = "never"; cookies = [];
    for (var id in cookie) {cookies[cookies.length] = id + ':' + cookie[id]}
    cookies = cookies.join(',');
    if (window.location.protocol == 'file:') {
      jsMath.Global.cookie = 'jsMath='+cookies;
    } else {
      cookies += '; path=/';
      if (cookie.keep && cookie.keep != '0D') {
        var ms = {
          D: 1000*60*60*24,
          W: 1000*60*60*24*7,
          M: 1000*60*60*24*30,
          Y: 1000*60*60*24*365
        };
        var exp = new Date;
        exp.setTime(exp.getTime() +
            cookie.keep.substr(0,1) * ms[cookie.keep.substr(1,1)]);
        cookies += '; expires=' + exp.toGMTString();
      }
      jsMath.window.document.cookie = 'jsMath='+cookies;
    }
  },

  /*
   *  Check if the control panel should be hidden
   */
  SetControls: function (rows) {
    if (!jsMath.rows) {
      jsMath.rows = rows;
      var cookie = document.cookie;
      if (String(window.location.protocol).match(/^(file|mk):$/))
        {cookie = jsMath.Global.cookie}
      if (cookie.match(/jsMath=([^;]+)/)) {cookie = RegExp.$1}
      if (!cookie.match("hiddenGlobal:0")) {
        if (this.inited) {setTimeout('jsMath.Hide()',1)} else {jsMath.hide = 1}
        return;
      }
    }
    document.body.rows = rows;
  },

  /*
   *  Remove the frame that holds the control panel
   */ 
  Hide: function () {
    if (jsMath.Global.isHidden) return;
    if (this.isMSIEmac) {
      //
      //  MSIE/Mac can't remove the frame, so hide it (and the drag bar)
      //
      document.body.rows = "-6,*";
    } else if (this.isOpera) {
      //
      //  Opera can remove it, but it is unstable for that, but
      //  setting the size to -1 seems to remove it as well.
      //
      document.body.rows = "-1,*";
    } else {
      document.body.removeChild(this.controls);
      document.body.rows = "*";
      jsMath.window = jsMath.FrameWindow(1);
    }
    this.Global.isHidden = 1;
  },

  /*
   *  Put back the control-panel frame
   */
  Show: function (bubble) {
    if (!jsMath.Global.isHidden) {
      if (bubble && jsMath.window) {jsMath.window.jsMath.Controls.Panel()}
      return;
    }
    if (!jsMath.isMSIEmac) {
      document.body.insertBefore(jsMath.controls,jsMath.frame);
      //
      //  Opera doesn't refresh the frames properly, so reload them
      //
      if (jsMath.isOpera) {
        setTimeout(
          'jsMath.controls.src = "about:blank";\n' +
          'jsMath.controls.src = jsMath.root+"jsMath-global-controls.html"+jsMath.domain;\n' +
          'jsMath.frame.src = jsMath.window.location;',1
        );
      }
    }
    document.body.rows = jsMath.rows;
    jsMath.window = jsMath.FrameWindow(1);
    jsMath.Global.isHidden = 0;
  },

  /*
   *  Empty the cache
   */
  Empty: function () {
    var OK = confirm('Really empty the equation cache?');
    if (OK) {jsMath.Global.ClearCache()}
  },

  /*
   *  Find the largest common domain between the source file
   *  and the location of the jsMath files
   */
  Domain: function () {
    this.domain = '';
    // MSIE/Mac can't do domain changes, so don't bother trying
    if (navigator.appName == 'Microsoft Internet Explorer' &&
        !navigator.platform.match(/Mac/) && navigator.userProfile != null) return;
    if (this.src == '') {
      if (window == parent) return;
      var oldDomain = document.domain;
      try {
        while (true) {
          try {if (parent.document.title != null) return} catch (err) {}
	  if (!document.domain.match(/\..*\./)) break;
          document.domain = document.domain.replace(/^[^.]*\./,'');
        }
      } catch (err) {}
      document.domain = oldDomain;
    } else {
      if (!this.src.match(new RegExp('^[^:]+:\/\/([^/]+)(:[0-9]+)?\/'))) return;
      if (document.domain == RegExp.$1) return;
      var src = RegExp.$1.split(/\./);
      var loc = String(window.location.host).split(/\./);
      var si, li; si = src.length-2; li = loc.length-2
      while (si >= 0 && li >= 0 && src[si] == loc[li]) {li--; si--}
      document.domain = src.slice(si+1).join('.');
      this.domain = '?'+document.domain;
    }
  },

  /*
   *  Create the document content based on whether this is the initial
   *  call to this file, or the secondary one
   */
  Content: function () {
    if (this.src != '') {
      this.root = (!this.isOpera) ? '' : 
        String(window.location).replace(/\/jsMath-global.html\??.*/,'/');
      document.writeln('<frameset rows="0,*" onload="jsMath.Init()">');
      document.writeln('<frame src="'+this.root+'jsMath-global-controls.html'+this.domain+'" frameborder="0" scrolling="no" id="jsMath_controls" />');
      document.writeln('<frame src="'+this.root+'jsMath-global.html" frameborder="0" onload="jsMath.Loaded()" id="jsMath_frame" />');
      document.writeln('</frameset>');
    } else {
      document.writeln('<body><br/></body>');
    }
  }

};

</script>
</head>

<script>
jsMath.src = jsMath.GetURL();
jsMath.Domain();
jsMath.Content();
</script>

</html>
